<?php
class mcz_DB
{
	private static $pool;
    private $pdo;
    private $isConnected = false;

    private $dsn        = 'mysql:host=localhost;dbname=test';
    private $user       = 'root';
    private $pass       = '';
    private $options    = array();
    private $names      = 'utf8';
    private $attributes = array(
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION
    );
	private $protectNames = false;
    function __construct($dsn, $user = NULL, $pass = NULL, array $options = NULL)
	{
        $this->dsn = $dsn;
        if(!is_null($user))
        {
            $this->user = $user;
        }
        if(!is_null($pass))
        {
            $this->pass = $pass;
        }
        if(!is_null($options))
        {
            $this->options = $options;
        }
	}


	/**
	 * performs insert query
	 *
	 * @param string table name
	 * @param array associative array with values to insert
	 * @param boolean wether to protect names using "`"
	 * @return integer
	 */
	public function insert($table, array $values, $protect_names = null)
	{
		if(is_null($protect_names))
		{
			$protect_names = $this->protectNames;
		}
		$table = trim($table);
		if (empty($table))
		{
			throw new PDOException('Table name can not be empty');
		}
		$values = $this->getColumnValues($values, $protect_names);
		if ($values['count'] < 1)
		{
			throw new PDOException('Nothing to insert');
		}
		$sql = 'insert into ' . ($protect_names ? '`' . $table . '`' : $table)
                              . '(' . implode(', ', $values['columns']) . ')
                              values(' . implode(', ', $values['data']) . ');';
		return $this->getAdapter()->exec($sql);
	}

	/**
	 * performs update query
	 *
	 * @param string table name
	 * @param array associative array with values to update
	 * @param string where clause
	 * @param boolean wether to protect names using "`"
	 * @return integer
	 */
	public function update($table, array $values, $where = '',
						   $protect_names = null)
	{
		//var_dump($protect_names);
		if(is_null($protect_names))
		{
			$protect_names = $this->protectNames;
		}
		$table = trim($table);
		if (empty($table))
		{
			throw new PDOException('Table name can not be empty');
		}
		$values = $this->getColumnValues($values, $protect_names);
		if ($values['count'] < 1)
		{
			throw new PDOException('Nothing to update');
		}
		$where = empty($where) ? '' : ' where ' . $where;
		$sql = 'update ' . ($protect_names ? '`' . $table . '`' : $table)
						 . ' set ' . implode(', ', $values['updates']) . $where;
		return $this->getAdapter()->exec($sql);
	}

	/**
	 * Performs SELECT query and return fetched results
	 *
	 * @param string $query select query... without select :-)
	 * @param array $quote values for place holders
	 * @return array results (empty array if none)
	 */
	public function fetch($query, array $quote = null)
	{
		if(empty($quote))
		{
			$results =  $this->getAdapter()->query('SELECT ' . $query)
						->fetchAll();
		}
		else
		{
			$stmt = $this->getAdapter()->prepare('SELECT ' . $query);
			$stmt->execute($quote);
			$results = $stmt->fetchAll();
		}
		return $results;
	}

	/**
	 * Convenience shortcut to PDO's exec method
	 *
	 * @param string Execution statement
	 * @return integer Number of rows modified
	 */
    public function exec($statement)
    {
        return $this->getAdapter()->exec($statement);
    }

	/**
	 * Convenience shortcut to PDO's quote method
	 *
	 * @param string the string to escape
	 * @param integer Escape mode, defaults to PDO::PARAM_STR
	 * @return string
	 */
    public function quote($string, $type = PDO::PARAM_STR)
    {
        return $this->getAdapter()->quote($string, $type);
    }

	/**
	 * Convinience shortcut to PDO's lastInsertId method
	 *
	 * @return string
	 */
	public function lastInsertId()
	{
		return $this->getAdapter()->lastInsertId();
	}

	/**
	 * Return SQL stamp
	 *
	 * @param format stamp according to given unix timestamp
	 * @param boolean return only date when true
	 * @return string
	 */
	public function stamp($time = '', $date_only = false)
	{
		return date($date_only ? 'Y-m-d' : 'Y-m-d H:i:s',
					empty($time) ? time() : (int)$time);
	}

    private function connect()
    {
        if(!$this->isConnected)
        {
			$this->setAdapter(new PDO($this->dsn, $this->user,
									  $this->pass, $this->options));
			$this->protectNames = false;
			foreach($this->attributes as $attribute => $value)
			{
				$this->pdo->setAttribute($attribute, $value);
			}
			if( in_array(substr($this->dsn, 0, 5), array('mysql', 'pgsql')))
			{
				$this->pdo->exec('set names ' . $this->names);
				$this->protectNames = true;
			}
			$this->isConnected = true;
        }
    }

	private function getColumnValues(array $values, $protect_names = null)
	{
		if(is_null($protect_names))
		{
			$protect_names = $this->protectNames;
		}
		$i = 0;
		$keys = array();
		$data = array();
		$updates = array();
		foreach($values as $key => $value)
		{
			$i++;
			$key = $protect_names ? '`' . $key . '`' : $key;
			$value = is_null($value) ? 'NULL' : $this->quote($value);
			$keys[] = $key;
			$data[] = $value;
			$updates[$key] = $key . '=' . $value;
		}
		return array(
			'count' => $i,
			'columns' => $keys,
			'data' => $data,
			'updates' => $updates
		);
	}

	/**
	 * For those special cases where you need all the power of PDO
	 *
	 * @return PDO database adapter
	 */
    public function getAdapter()
    {
        $this->connect();
        return $this->pdo;
    }

    private function setAdapter(PDO $adapter)
    {
        $this->pdo = $adapter;
    }

	/**
	 * Get/set db instance from/to the connection pool
	 *
	 * Usage: $dbHandle = mcz_DB::getInstance($mydb, $mydsn, $myuser, $mypass);
	 *
	 * @param string pool id
	 * @param string database dsn
	 * @param string database user
	 * @param strinf user password
	 * @return mcz_DB
	 */
	public static function getInstance($handle_id = 'default',
									   $dsn = mcz_DB_DSN,
									   $user = mcz_DB_USER,
									   $pass = mcz_DB_PASS)
	{
		if (!isset(self::$pool[$handle_id]))
		{
			self::$pool[$handle_id] = new mcz_DB($dsn, $user, $pass);
		}
		return self::$pool[$handle_id];
	}
}